
#include "nane/core/crt/Backtrace_.h"
#include "nane/core/WinAPI/winapi.h"
#include <cstring>

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static const USHORT c_maxBacktrace = 64;
    ////////////////////////////////////////////////////////////////////////////////////////////////
    typedef USHORT (WINAPI *TCaptureStackBackTrace)(ULONG, ULONG, PVOID*, PULONG);
    ////////////////////////////////////////////////////////////////////////////////////////////////
    extern void Win32BacktraceGetSymbolsImpl( StackFrame* _stackFrames, USHORT _count );
    ////////////////////////////////////////////////////////////////////////////////////////////////
    uint32 GetStackFrames( StackFrame** _stackFrames, uint32 _count )
    {
        uint32 framesCaptured = 0;
        HMODULE hKernel32 = ::LoadLibraryA("Kernel32");
        if( hKernel32 != NULL )
        {
            TCaptureStackBackTrace pCaptureStackBackTrace = 
                    reinterpret_cast<TCaptureStackBackTrace>(::GetProcAddress(hKernel32, "RtlCaptureStackBackTrace"));
            if( pCaptureStackBackTrace != NULL )
            {
                PVOID backtrace[c_maxBacktrace];
                USHORT backtraceCount = (std::min)(static_cast<USHORT>(_count), c_maxBacktrace);
                USHORT backtraceCaptured = pCaptureStackBackTrace(0, backtraceCount, backtrace, NULL);
                *_stackFrames = new StackFrame[backtraceCaptured+1];
                for( USHORT i = 0; i < backtraceCaptured; ++i )
                {
                    (*_stackFrames)[i].address = backtrace[i];
                    (*_stackFrames)[i].text = NULL;
                }
                (*_stackFrames)[backtraceCaptured].address = NULL;
                (*_stackFrames)[backtraceCaptured].text = NULL;

                Win32BacktraceGetSymbolsImpl(*_stackFrames, backtraceCaptured);
                
                framesCaptured = static_cast<uint32>(backtraceCaptured);
            }
            ::FreeLibrary(hKernel32);
        }
        return framesCaptured;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void FreeStackFrames( StackFrame* _stackFrames )
    {
        StackFrame* stackFrame = _stackFrames;
        while( stackFrame->address != 0 )
        {
            delete[] stackFrame->text;
            ++stackFrame;
        }
        delete[] _stackFrames;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
